<?php

// 测试
$config = include 'config.php';
$model = new Model($config);

// 测试查询
// $result = $model->limit('0, 5')
//     ->table('users')
//     ->field('id, name, email')
//     ->order('id desc')
//     ->where('id>0')
//     ->select();
// var_dump($model->sql);
// var_dump($result);

// 测试插入
// $data = ['name' => 'test', 'email' => 'test@test.com', 'password' => 'test'];
// $result = $model->table('users')->insert($data);
// var_dump($model->sql);
// var_dump($result);

// 测试删除
// $result = $model->table('users')->where('id=8')->delete();
// var_dump($model->sql);
// var_dump($result);

// 测试更新
// test@163.com
// $data = ['name' => 'hgz0315', 'email' => 'test@163.com'];
// $result = $model->table('users')->where('id=1')->update($data);
// var_dump($model->sql);
// var_dump($result);

// 测试 max 方法
// $result = $model->table('users')->max('id');
// var_dump($model->sql);
// var_dump($result);

// 测试 getByXxxx
$result = $model->table('users')->getById(7);
var_dump($model->sql);
var_dump($result);



class Model
{
    // 主机名
    protected $host;
    // 用户名
    protected $user;
    // 密码
    protected $pwd;
    // 数据库名
    protected $dbname;
    // 字符集
    protected $charset;
    // 数据表前缀
    protected $prefix;

    // 数据库连接资源
    protected $link;
    // 数据表名  这里可以自己指定表明
    protected $tableName;
    // sql 语句
    protected $sql;
    // 操作数组  存放的就是所有的查询条件
    protected $options;

    // 构造方法，初始化成员变量
    function __construct($config)
    {
        // 初始化成员变量
        $this->host = $config['DB_HOST'];
        $this->user = $config['DB_USER'];
        $this->pwd = $config['DB_PWD'];
        $this->dbname = $config['DB_NAME'];
        $this->charset = $config['DB_CHARSET'];
        $this->prefix = $config['DB_PREFIX'];

        // 连接数据库
        $this->link = $this->connect();

        // 得到数据表名
        $this->tableName = $this->getTableName();

        // 初始化 options 数组
        $this->initOptions();
    }

    function __destruct()
    {
        mysqli_close($this->link);
    }

    // 获取成员属性
    function __get($name)
    {
        if ($name == 'sql') {
            return $this->sql;
        }

        return false;
    }

    // getByXxxx
    function __call($name, $arguments)
    {
        // 获取前五个字符
        $str = substr($name, 0, 5);
        // 获取剩下的字段名
        $field = self::uncamelize(substr($name, 5));
        // 判断前 5 个字符是否是 getBy
        if ($str == 'getBy') {
            return $this->where($field . '="' . $arguments[0] . '"')->select();
        }
        return false;
    }

    // field 方法
    function field($field)
    {
        if (!empty($field)) {
            if (is_string($field)) {
                $this->options['field'] = $field;
            } else if (is_array($field)) {
                $this->options['field'] = join(',', $field);
            }
        }

        return $this;
    }

    // table 方法
    function table($table)
    {
        if (!empty($table)) {
            $this->options['table'] = $table;
        }

        return $this;
    }

    // where 方法
    function where($where)
    {
        if (!empty($where)) {
            $this->options['where'] = 'WHERE ' . $where;
        }

        return $this;
    }

    // group 方法
    function group($group)
    {
        if (!empty($group)) {
            $this->options['group'] = 'GROUP BY ' . $group;
        }

        return $this;
    }

    // having 方法
    function having($having)
    {
        if (!empty($having)) {
            $this->options['having'] = 'HAVING ' . $having;
        }

        return $this;
    }

    // order 方法
    function order($order)
    {
        if (!empty($order)) {
            $this->options['order'] = 'ORDER BY ' . $order;
        }

        return $this;
    }

    // limit 方法
    function limit($limit)
    {
        if (!empty($limit)) {
            if (is_string($limit)) {
                $this->options['limit'] = 'LIMIT ' . $limit;
            } else if (is_array($limit)) {
                $this->options['limit'] = 'LIMIT ' . join(',', $limit);
            }
        }

        return $this;
    }

    // select 方法
    function select()
    {
        // 先预写一个带有占位符的 sql 语句
        $sql = 'SELECT %FIELD% from %TABLE% %WHERE% %GROUP% %HAVING% %ORDER% %LIMIT%';
        // 将 options 对应中的值依次替换上面的占位符
        $sql = str_replace(
            [
                '%FIELD%',
                '%TABLE%',
                '%WHERE%',
                '%GROUP%',
                '%HAVING%',
                '%ORDER%',
                '%LIMIT%'
            ],
            [
                $this->options['field'],
                $this->options['table'],
                $this->options['where'],
                $this->options['group'],
                $this->options['having'],
                $this->options['order'],
                $this->options['limit']
            ],
            $sql
        );
        // 保存 sql 语句
        $this->sql = $sql;
        // 执行 sql 语句
        return $this->query($sql);
    }

    // insert 插入方法
    // $data: 关联数组，键就是字段名，值就是字段值
    function insert($data)
    {
        // 处理值是字符串问题，两边需要添加单或双引号
        $data = $this->parseValue($data);
        // 提取所有的键
        $keys = array_keys($data);
        // 提取所有的值
        $values = array_values($data);
        // sql 语句
        $sql = 'INSERT INTO %TABLE%(%FIELD%) VALUES(%VALUES%)';
        $sql = str_replace([
            '%TABLE%',
            '%FIELD%',
            '%VALUES%',
        ], [
            $this->options['table'],
            join(',', $keys),
            join(',', $values),
        ], $sql);
        $this->sql = $sql;
        return $this->exec($sql, true);
    }

    // delete 删除方法
    function delete()
    {
        $sql = 'DELETE FROM %TABLE% %WHERE%';
        $sql = str_replace([
            '%TABLE%',
            '%WHERE%'
        ], [
            $this->options['table'],
            $this->options['where']
        ], $sql);

        $this->sql = $sql;

        return $this->exec($sql);
    }

    // update 更新方法
    function update($data)
    {
        // 处理值是字符串问题，两边需要添加单或双引号
        $data = $this->parseValue($data);
        // 将关联数组拼接为固定的格式 键=值，键=值
        $value = $this->parseUpdate($data);
        // 准备 sql 语句
        $sql = 'UPDATE %TABLE% SET %VALUE% %WHERE%';
        $sql = str_replace([
            '%TABLE%',
            '%VALUE%',
            '%WHERE%'
        ], [
            $this->options['table'],
            $value,
            $this->options['where']
        ], $sql);

        $this->sql = $sql;

        return $this->exec($sql);
    }

    // max 方法
    function max($field)
    {
        $result = $this->field('max(' . $field . ') as max')->select();
        return $result[0]['max'];
    }

    // query
    function query($sql)
    {
        // 清空 options 数组的值
        $this->initOptions();
        // 执行 sql 语句
        $result = mysqli_query($this->link, $sql);
        // 提取结果集存放到数组中
        if ($result && mysqli_affected_rows($this->link)) {
            while ($data = mysqli_fetch_assoc($result)) {
                $newData[] = $data;
            }
        }
        // 返回结果集
        return $newData;
    }

    // exec
    function exec($sql, $isInsert = false)
    {
        // 清空 options 数组的值
        $this->initOptions();
        // 执行 sql 语句
        $result = mysqli_query($this->link, $sql);
        if ($result && mysqli_affected_rows($this->link)) {
            // 判断是否是插入语句，根据不同的语句返回不同的结果
            if ($isInsert) {
                return mysqli_insert_id($this->link);
            } else {
                return mysqli_affected_rows($this->link);
            }
        }
        return false;
    }

    /**
  　　* 下划线转驼峰
  　　* 思路:
  　　* step1.原字符串转小写,原字符串中的分隔符用空格替换,在字符串开头加上分隔符
  　　* step2.将字符串中每个单词的首字母转换为大写,再去空格,去字符串首部附加的分隔符.
  　　*/
    function camelize($uncamelized_words, $separator = '_')
    {
        $uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words));
        return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator);
    }

    /**
 　　* 驼峰命名转下划线命名
 　　* 思路:
 　　* 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
 　　*/
    static function uncamelize($camelCaps, $separator = '_')
    {
        return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
    }

    // 连接数据库
    protected function connect()
    {
        // 连接数据库
        $link = mysqli_connect($this->host, $this->user, $this->pwd);
        if (!$link) {
            die('数据库连接失败');
        }
        // 选择数据库
        mysqli_select_db($link, $this->dbname);
        // 选择字符集
        mysqli_set_charset($link, $this->charset);

        return $link;
    }

    // 得到表明
    protected function getTableName()
    {
        // 如果设置了成员变量，那么通过成员变量来得到表名
        if (!empty($this->tableName)) {
            return $this->prefix . $this->tableNam;
        }
        // 如果没有设置成员变量，那么通过类名来得到表明
        // 得到当前类名字符串
        $className = get_class($this);
        $table = strtolower(substr($className, 0, -5));
        return $this->prefix . $table;
    }

    // 初始化 initOptions
    protected function initOptions()
    {
        $arr = ['where', 'table', 'field', 'order', 'group', 'having', 'limit'];
        foreach ($arr as $value) {
            // 将 options 数组中这些键对应的值全部清空
            $this->options[$value] = '';
            // 将 table 默认设置为 tableName
            if ($value == 'table') {
                $this->options[$value] = $this->tableName;
            } else if ($value == 'field') {
                $this->options[$value] = '*';
            }
        }
    }

    // 传递进来一个数组，将数组中值为字符串的两边加上引号
    protected function parseValue($data)
    {
        foreach ($data as $key => $value) {
            if (is_string($value)) {
                $value = '"' . $value . '"';
            }
            $newData[$key] = $value;
        }
        return $newData;
    }

    // 将关联数组拼接为固定的格式 键=值，键=值
    protected function parseUpdate($data)
    {
        foreach ($data as $key => $value) {
            $newData[] = $key . '=' . $value;
        }
        return join(',', $newData);
    }
}
